#pragma once
#include "..\Common\Global.h"

template <typename I,typename O>
class CombinatorResult
{
public:
	CombinatorResult(Combinator_MemoryManager<I,O>* _MM) : MM(_MM),Result(FALSE){}
	CombinatorResult(I& _value,Combinator_MemoryManager<I,O>* _MM) : value(_value),Result(FALSE),MM(_MM){}
	CombinatorResult(CombinatorResult<I,O>& _value) : value(_value.value),Result(_value.Result),MM(_value.GetMM()){}

	inline void SetResult(BOOL _Result)
	{
		Result = _Result;
	}

	inline I& Value()
	{
		return value;
	}

	operator BOOL()const
	{
		return Result;
	}

	BOOL operator==(O _value)
	{
		return value == _value;
	}

	//virtual O& operator+(I _value)=0;

	virtual O& operator+(O& _value)=0;

	Combinator_MemoryManager<I,O>* GetMM()
	{
		return MM;
	}
protected:
	BOOL Result;
	I value;
	Combinator_MemoryManager<I,O>* MM;
};

template <typename I,typename O>
class Combinator
{
public:
	Combinator(Combinator_MemoryManager<I,O>* _MM) : MM(_MM){}

	virtual O Parser(I& input)=0;

	inline Combinator_MemoryManager<I,O>* GetMM()
	{
		return MM;
	}
protected:
	Combinator_MemoryManager<I,O>* MM;
};

template <typename I,typename O>
class Combinator_Node// : public Combinator<I,O>
{
public:
	Combinator_Node(Combinator_MemoryManager<I,O>* _MM) : value(0),MM(_MM){}
	Combinator_Node(Combinator<I,O>* _value) : value(_value),MM(_value->GetMM()){}
	Combinator_Node(Combinator_Node<I,O>& _value) : value(_value.value),MM(_value.GetMM()){}

	virtual O Parser(I& input)
	{
		return value->Parser(input);
	}

	Combinator<I,O>* Value()
	{
		return value;
	}

	Combinator_Node<I,O>& operator+(Combinator_Node<I,O>& _value)
	{
		Combinator_Seq<I,O>* pSeq = new Combinator_Seq<I,O>(*this,_value,GetMM());
		Combinator_Node<I,O>* pNode = new Combinator_Node<I,O>(pSeq);
		GetMM()->AddSeq(pSeq);
		GetMM()->AddCombinatorNode(pNode);
		return *pNode;
	}

	Combinator_Node<I,O>& operator|(Combinator_Node<I,O>& _value)
	{
		Combinator_Alt<I,O>* pAlt = new Combinator_Alt<I,O>(*this,_value,GetMM());
		Combinator_Node<I,O>* pNode = new Combinator_Node<I,O>(pAlt);
		GetMM()->AddAlt(pAlt);
		GetMM()->AddCombinatorNode(pNode);
		return *pNode;
	}

	Combinator_Node<I,O>& operator&(Combinator_Node<I,O>& _value)
	{
		Combinator_And<I,O>* pAnd = new Combinator_And<I,O>(*this,_value,GetMM());
		Combinator_Node<I,O>* pNode = new Combinator_Node<I,O>(pAnd);
		GetMM()->AddAnd(pAnd);
		GetMM()->AddCombinatorNode(pNode);
		return *pNode;
	}

	Combinator_Node<I,O>& operator*()
	{
		Combinator_Any<I,O>* pAny = new Combinator_Any<I,O>(*this,0,GetMM());
		Combinator_Node<I,O>* pNode = new Combinator_Node<I,O>(pAny);
		GetMM()->AddAny(pAny);
		GetMM()->AddCombinatorNode(pNode);
		return *pNode;
	}

	Combinator_Node<I,O>& operator+()
	{
		Combinator_Any<I,O>* pAny = new Combinator_Any<I,O>(*this,0,GetMM());
		Combinator_Node<I,O>* pNode1 = new Combinator_Node<I,O>(pAny);
		Combinator_Seq<I,O>* pSeq = new Combinator_Seq<I,O>(*this,*pNode1,GetMM());
		Combinator_Node<I,O>* pNode2 = new Combinator_Node<I,O>(pSeq);
		GetMM()->AddAny(pAny);
		GetMM()->AddCombinatorNode(pNode1);
		GetMM()->AddSeq(pSeq);
		GetMM()->AddCombinatorNode(pNode2);
		return *pNode2;
	}

	static Combinator_Node<I,O>& opt(Combinator_Node<I,O>& _value)
	{
		Combinator_Opt<I,O>* pOpt = new Combinator_Opt<I,O>(_value,_value.GetMM());
		Combinator_Node<I,O>* pNode = new Combinator_Node<I,O>(pOpt);
		_value.GetMM()->AddOpt(pOpt);
		_value.GetMM()->AddCombinatorNode(pNode);
		return *pNode;
	}

	static Combinator_Node<I,O>& not(Combinator_Node<I,O>& _value)
	{
		Combinator_Not<I,O>* pNot = new Combinator_Not<I,O>(_value,_value.GetMM());
		Combinator_Node<I,O>* pNode = new Combinator_Node<I,O>(pNot);
		_value.GetMM()->AddNot(pNot);
		_value.GetMM()->AddCombinatorNode(pNode);
		return *pNode;
	}

	static Combinator_Node<I,O>& clone(Combinator_Node<I,O>& _value)
	{
		Combinator_Node<I,O>* pNode = new Combinator_Node<I,O>(_value);
		_value.GetMM()->AddCombinatorNode(pNode);
		return *pNode;
	}

	inline Combinator_MemoryManager<I,O>* GetMM()
	{
		return MM;
	}
protected:
	Combinator<I,O>* value;
	Combinator_MemoryManager<I,O>* MM;
};
